什么是ClickHouse
ClickHouse 是Yandex开源的一个用于实时数据分析的基于列存储的数据库,其处理数据的速度比传统方法快100-1000 倍。
ClickHouse的性能超过了目前市场上可比的面向列的 DBMS,每秒钟每台服务器每秒处理数亿至十亿多行和数十千兆字节的数据。
ClickHouse基本介绍
ClickHouse作为新兴事物,特点有很多,很多大厂也在使用;关于其特点也不多说了,确切的说是为OLAP量身打造的数据库
OLAP的特点
读多于写:不同于事务处理(OLTP)的场景,数据分析(OLAP)场景通常是将数据批量导入后,进行任意维度的灵活探索、BI工具洞察、报表制作等。
大宽表:读大量行但是少量列,结果集较小,在OLAP场景中,通常存在一张或是几张多列的大宽表,列数高达数百甚至数千列。对数据分析处理时,选择其中的少数几列作为维度列、其他少数几列作为指标列,然后对全表或某一个较大范围内的数据做聚合计算。这个过程会扫描大量的行数据,但是只用到了其中的少数列。而聚合计算的结果集相比于动辄数十亿的原始数据,也明显小得多。
数据批量写入:OLTP类业务数据不更新或少更新,对于延时要求更高,要避免让客户等待造成业务损失;而OLAP类业务,由于数据量非常大,通常更加关注写入吞吐量。
无需事务,数据一致性要求低:OLAP类业务对于事务需求较少,通常是导入历史日志数据,或搭配一款事务型数据库并实时从事务型数据库中进行数据同步。
灵活多变,不适合预先建模:分析场景下,随着业务变化要及时调整分析维度、挖掘方法,以尽快发现数据价值、更新业务指标。
ClickHouse使用场景压根就是针对OLAP设计的,并且根据相关的特性做了优化。
完备的DBMS功能
ClickHouse拥有完备的管理功能,作为一个DBMS,它具备了一些基本功能,如下所示。
DDL:可以动态地创建、修改或删除数据库、表和视图,而无须重启服务。
DML :可以动态查询、插入、修改或删除数据。
权限控制:可以按照用户粒度设置数据库或者表的操作权限,保障数据的安全性。
数据备份与恢复:提供了数据备份导出与导入恢复机制,满足生产环境的要求。
分布式管理:提供集群模式,能够自动管理多个数据库节点。
列式存储与数据压缩
一个非常流行的观点认为,如果你想让查询变得更快,最简单且有效的方法是减少数据扫描范围和数据传输时的大小,而列式存储和数据压缩就可以帮助我们实现上述两点。
列式存储和数据压缩通常是伴生的,因为一般来说列式存储是数据压缩的前提。
按列存储相比按行存储的另一个优势是对数据压缩的友好性。
ClickHouse就是一款使用列式存储的数据库,数据按列进行组织,属于同一列的数据会被保存在一起,列与列之间也会由不同的文件分别保存。
向量化执行引擎
向量化执行就是这种方式的典型代表,这项寄存器硬件层面的特性,为上层应用程序的性能带来了指数级的提升。
为了实现向量化执行,需要利用CPU的SIMD指令,即用单条指令操作多条数据。
ClickHouse目前利用SSE4.2指令集实现向量执行引擎,对内存中的列式数据,一个batch调用一次SIMD指令,不仅减少了函数调用次数、降低了cache miss,而且可以充分发挥SIMD指令的并行能力,大幅缩短了计算耗时。
关系模型与SQL查询
相比HBase和Redis这类NoSQL数据库,ClickHouse使用关系模型描述数据并提供了传统数据库的概念,这使得它平易近人,容易理解和学习。
ClickHouse使用了关系模型,所以将构建在传统关系型数据库或数据仓库之上的系统迁移到ClickHouse的成本会变得更低,可以直接沿用之前的经验成果。
多样化的表引擎
在ClickHouse的设计中,能够察觉到一些MySQL的影子,表引擎的设计就是其中之一。
与MySQL类似,ClickHouse也将存储部分进行了抽象,把存储引擎作为一层独立的接口。
多线程与分布式
ClickHouse几乎具备现代化高性能数据库的所有典型特征,对于多线程和分布式这类被广泛使用的技术,自然更是不在话下。
如果说向量化执行是通过数据级并行的方式提升了性能,那么多线程处理就是通过线程级并行的方式实现了性能的提升。
ClickHouse还提供了可线性拓展的分布式计算能力。ClickHouse会自动将查询拆解为多个task下发到集群中,然后进行多机并行处理,最后把结果汇聚到一起。
相比基于底层硬件实现的向量化执行SIMD,线程级并行通常由更高层次的软件层面控制。ClickHouse在数据存取方面,既支持分区 ( 纵向扩展,利用多线程原理 ),也支持分片 ( 横向扩展,利用分布式原理 ),可以说是将多线程和分布式的技术应用到了极致。
多主架构
HDFS、Spark、HBase和Elasticsearch这类分布式系统,都采用了Master-Slave主从架构,由一个管控节点作为Leader统筹全局。
而ClickHouse则采用Multi-Master多主架构,集群中的每个节点角色对等,客户端访问任意一个节点都能得到相同的效果。这种多主的架构有许多优势,例如对等的角色使系统架构变得更加简单,不用再区分主控节点、数据节点和计算节点,集群中的所有节点功能相同。所以它天然规避了单点故障的问题,非常适合用于多数据中心、异地多活的场景
数据分片与分布式查询
数据分片是将数据进行横向切分,这是一种在面对海量数据的场景下,解决存储和查询瓶颈的有效手段,是一种分治思想的体现。
ClickHouse支持分片,而分片则依赖集群。每个集群由1到多个分片组成,而每个分片则对应了ClickHouse的1个服务节点。分片的数量上限取决于节点数量 ( 1个分片只能对应1个服务节点 )。
ClickHouse并不像其他分布式系统那样,拥有高度自动化的分片功能。ClickHouse提供了本地表 与分布式表的概念。一张本地表等同于一份数据的分片。而分布式表本身不存储任何数据,它是本地表的访问代理,其作用类似分库中间件。借助分布式表,能够代理访问多个数据分片,从而实现分布式查询。
列式存储
相比于行式存储,列式存储在分析场景下有着许多优良的特性。
分析场景中往往需要读大量行但是少数几个列。而列存模式下,只需要读取参与计算的列即可,极大的减低了IO cost,加速了查询。
同一列中的数据属于同一类型,压缩效果显著,更小的数据意味着读取也就更快,意味着同等大小的内存能够存放更多数据,系统cache效果更好。
自由的压缩算法选择。不同列的数据具有不同的数据类型,适用的压缩算法也就不尽相同。可以针对不同列类型,选择最合适的压缩算法。
数据有序存储
ClickHouse支持在建表时,指定将数据按照某些列进行sort by。排序后,保证了相同sort key的数据在磁盘上连续存储,且有序摆放。
在进行等值、范围查询时,where条件命中的数据都紧密存储在一个或若干个连续的Block中,而不是分散的存储在任意多个Block, 大幅减少需要IO的block数量。
数据分片
ClickHouse支持单机模式,也支持分布式集群模式。在分布式模式下,ClickHouse会将数据分为多个分片,并且分布到不同节点上。不同的分片策略在应对不同的SQL Pattern时,各有优势。ClickHouse提供了丰富的sharding策略,让业务可以根据实际需求选用。
random随机分片:写入数据会被随机分发到分布式集群中的某个节点上。
constant固定分片:写入数据会被分发到固定一个节点上。
hash分片:按照某一列的值进行hash分片。
自定义表达式分片:指定任意合法表达式,根据表达式被计算后的值进行hash分片。
数据分片,让ClickHouse可以充分利用整个集群的大规模并行计算能力,快速返回查询结果。
分片多样化好处也多多。
数据Partitioning
ClickHouse支持PARTITION BY子句,在建表时可以指定按照任意合法表达式进行数据分区操作,比如通过toYYYYMM()将数据按月进行分区、toMonday()将数据按照周几进行分区、对Enum类型的列直接每种取值作为一个分区等。
数据TTL
ClickHouse通过TTL提供了数据生命周期管理的能力。目前支持几种不同粒度的TTL:
Column与Field
作为一款百分之百的列式存储数据库,ClickHouse按列存储数据,内存中的一列数据由一个Column对象表示。
Column和Field是ClickHouse数据最基础的映射单元。本质上Column和Field就是一个个的接口。IColumn是一个抽象接口,insertRangeFrom和insertFrom方法、用于分页的cut,以及用于过滤的filter方法等等都是接口里面的方法。
这些方法的具体实现对象则根据数据类型的不同,由相应的对象实现,例如ColumnString、ColumnArray和ColumnTuple等。
在大多数场合,ClickHouse都会以整列的方式操作数据。如果需要操作单个具体的数值,则需要使用Field对象,Field对象代表一个单值。
与Column对象的泛化设计思路不同,Field对象使用了聚合的设计模式。在Field对象内部聚合了Null、UInt64、String和Array等13种数据类型及相应的处理逻辑。
DataType
数据的序列化和反序列化工作由DataType负责。IDataType接口定义了许多正反序列化的方法涵盖了常用的二进制、文本、JSON、XML、CSV和Protobuf等多种格式类型。
IDataType也使用了泛化的设计模式,具体方法的实现逻辑由对应数据类型的实例承载。比如DataTypeString、DataTypeArray等等。
DataType虽然负责序列化相关工作,但它并不直接负责数据的读取,而是转由从Column或Field对象获取。在DataType的实现类中,聚合了相应数据类型的Column对象和Field对象。
例如DataTypeString会引用字符串类型的ColumnString,而DataTypeArray则会引用数组类型的ColumnArray,以此类推。
Block与Block流
ClickHouse内部的数据操作是面向Block对象进行的,并且采用了流的形式。虽然Column和Filed组成了数据的基本映射单元,但对应到实际操作,它们还缺少了一些必要的信息,比如数据的类型及列的名称。
于是ClickHouse设计了Block对象,Block对象可以看作数据表的子集。Block对象的本质是由数据对象、数据类型和列名称组成的三元组,即Column、DataType及列名称字符串。
Column提供了数据的读取能力,而DataType知道如何正反序列化,所以Block在这些对象的基础之上实现了进一步的抽象和封装,从而简化了整个使用的过程,仅通过Block对象就能完成一系列的数据操作。
有了Block对象这一层封装之后,对Block流的设计就是水到渠成的事情了。流操作有两组顶层接口:IBlockInputStream负责数据的读取和关系运算,IBlockOutputStream负责将数据输出到下一环节。Block流也使用了泛化的设计模式,对数据的各种操作最终都会转换成其中一种流的实现。IBlockInputStream接口定义了读取数据的若干个read虚方法,而具体的实现逻辑则交由它的实现类来填充。
Table
在数据表的底层设计中并没有所谓的Table对象,它直接使用IStorage接口指代数据表。表引擎是ClickHouse的一个显著特性,不同的表引擎由不同的子类实现,例如 系统表、合并树表引擎、日志表引擎 。
IStorage接口定义了DDL 、read和write方法,它们分别负责数据的定义、查询与写入。在数据查询时,IStorage负责根据AST查询语句的指示要求,返回指定列的原始数据。
对Table发起的一次操作通常都会经历这样的过程,接收AST查询语句,根据AST返回指定列的数据,之后再将数据交由Interpreter做进一步处理。
Parser与Interpreter
Parser和Interpreter是非常重要的两组接口:Parser分析器负责创建AST对象;而Interpreter解释器则负责解释AST,并进一步创建查询的执行管道。它们与IStorage一起,串联起了整个数据查询的过程。
Cluster与Replication
ClickHouse的集群由分片 ( Shard ) 组成,而每个分片又通过副本 (Replica ) 组成。
ClickHouse的某些设计总是显得独树一帜,而集群与分片就是其中之一。这里有几个与众不同的特性。
ClickHouse的1个节点只能拥有1个分片,也就是说如果要实现1分片、1副本,则至少需要部署2个服务节点。
分片只是一个逻辑概念,其物理承载还是由副本承担的。
预告:本周将安排ClickHouse赠书活动x4,关注公众号最新动态,不要错过哦
ps:本公众号资源、福利不停,需要资料视频加群,备注:数仓,请添加微信『ID:iom1128』获取!
资源获取 获取Flink,Spark,Hive,Hadoop,面试题,数据治理,数据质量等资源请公众号后台回复关键词:flink,hive,hbase,数据治理,数据质量,机器学习等;也可以公众号菜单栏自行查看更多精彩专题。
下载资料:长按扫码回复 clickhouse